既然要點擊、又要判斷是不是按著 shift 鍵的點擊,那我們先把全部<input type="checkbox">
都新增針對點擊觸發的事件監聽器,然後把event
印出來觀察看看。
const checkboxList = [...document.querySelectorAll(".item input")];
checkboxList.forEach((node) =>
node.addEventListener("click", (e) => console.log(e))
);
嗯,屬性琳瑯滿目,但善用搜尋ctrl+f
馬上就能找到本日重點shiftKey ,什麼!你不信那麼簡單就找到了?那你按著 shift 鍵再點擊觸發一次看看,他馬上就變成 true 給你看, 眼尖的妳或頭腦聰明的你,或許已經發現可以判斷是否按著另外幾種種案件觸 發這次點擊事件,那就是:altKey、ctrlKey以及metaKey,原理都是相同
既然按著 shift 鍵點擊時上次與這次點擊中間的所有checkbox
都需要跟著選取或取消選取,那麼當然有一個變數需要存放上次的資料。
那要存放什麼資料能當作位置呢?應該一想就知道! 位置? index?,剛剛不是用querySelectorAll
取得所有checkbox
的 NodeList 了嗎,那我們只要知道這次觸發的是在 nodeList 中的第幾個位置(馬上搬出 Day7 學到的Array.prototype.findIndex()來用,但要注意,我們在上面已經把 NodeList 解構賦值變成真正的 Array 才能使用這個 method 哦!!!),然後讓 nodeList 中介於上次 index 與這次 index 間所有的checkbox
的 checked 屬性都變成這次觸發的事件中的 checked 屬性就完成囉,最後再把這次的 index 值當作下一次的上一次 index 值(有點饒舌),完整的checkHandler函式
如下,
//第一次進網頁,沒有上一次點擊位置
let lastIndex = null;
checkboxList.forEach((node) =>
node.addEventListener("click", checkHandler)
);
function checkHandler(e) {
//用findIndex找到這次觸發是在nodeList中的第幾個
const currentIndex = checkboxList.findIndex((node) => node === this);
//如果按著shift鍵點擊 且 有上一次的點擊位置才往下執行
if (e.shiftKey && lastIndex !== null) {
// 如果上次index比這次index大,執行這段由下往上點回來
if (lastIndex > currentIndex) {
for (let index = lastIndex; index > currentIndex; index--) {
checkboxList[index].checked = this.checked;
}
// 如果上次index比這次index小,執行這段由上往下點下去
} else {
for (let index = lastIndex; index < currentIndex; index++) {
checkboxList[index].checked = this.checked;
}
}
}
//最後再把這次的index值當作下一次觸發的上一次index值使用
lastIndex = currentIndex;
}
題外話大家會不會覺得從 index 值小到大跟從大到小點選的 for 迴圈有點礙眼呢,裡面的邏輯幾乎重複,在與工作室的大學長討論之後,他跟我分享了他在Alex 宅幹嘛 YouTube 頻道學到的以下方式,用Math.min
及Math.max
這樣永遠確保前面是小的數字後面是大的數字,並將原陣列依此 slice 切片出要點選的 node 節點,再 forEach 依序點選即可,全部用 Array method 完成,是不是非常簡潔有力呢!如果還有其他更好解決這種狀況的解法,還煩請留言賜教!!!
if (e.shiftKey && lastIndex !== null) {
checkboxList
.slice(
Math.min(lastIndex, currentIndex),
Math.max(lastIndex, currentIndex) + 1
)
.forEach((node) => {
node.checked = this.checked;
});
}